package org.global.basic;


import org.global.GEntity;
import org.global.basic.specification.SpecNotDel;
import org.global.basic.specification.SpecificationById;
import org.global.basic.tree.TreeService;
import org.global.utils.BeansUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

public abstract class BaseServiceOpImpl<T extends GEntity<ID>, ID extends Serializable, P extends ServiceParams<ID, T>>
        extends TreeService implements CurdServiceOp<ID, T, P>, PagingServiceOp<T> {

    public abstract CurdFindRepository<T, ID> repository();

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T save(P p) {
        return repository().save(p.get());
    }

    /**
     * 1.实体处于托管状态就直接新增
     * 2.实体处于瞬时状态，需要把顺序状态的实体属性copy到托管状态的实体
     * @param p 参数
     * @return 返回一个托管状态的实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T update(P p) throws IllegalAccessException {
        // managed/persistent，处于受托管状态
        if (entityManger().contains(p.get())) {
            return repository().save(p.get());
        } else {
            Optional<T> optional = findById(p.get().getId());
            if (optional.isEmpty()) {
                throw new NullPointerException("被修改的数据查询不到");
            }
            T store = optional.get();
            BeansUtils.copy(p.get(), store, "id", "createTime", "updateTime", "createBy", "updateBy");
            repository().save(store);
            p.set(store);
            return store;
        }
    }

    @Override
    @Transactional
    public void updateAll(P p) throws Exception {
        List<T> all = p.getAll();
        Objects.requireNonNull(all, "被修改的数据不能是空");
        List<ID> ids = all.stream().map(GEntity::getId).collect(Collectors.toList());
        Map<ID, T> cacheMap = all.stream().collect(Collectors.toMap(
                GEntity::getId,
                (e) -> e
        ));
        List<T> allByStore = repository().findAllById(ids);
        for (T value : allByStore) {
            ID id = value.getId();
            T t = cacheMap.get(id);
            BeansUtils.copy(t, value, "id", "createTime", "updateTime", "createBy", "updateBy");
        }
        repository().saveAll(allByStore);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T del(P p) {
        Objects.requireNonNull(p.getId(), "请选择被删除的数据");
        Optional<T> optional = findById(p.getId());
        if (optional.isEmpty()) {
            throw new RuntimeException("找不到数据");
        }
        T entity = optional.get();
        entity.setDel(true);
        repository().save(entity);
        p.set(entity);
        return entity;
    }

    @Override
    public List<T> delByIds(List<ID> ids) {
        Objects.requireNonNull(ids);
        List<T> allById = repository().findAllById(ids);
        allById.forEach((e) -> e.setDel(true));
        return repository().saveAll(allById);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<T> findById(ID id) {
        Objects.requireNonNull(id);
        SpecificationById<T, ID> specification = new SpecificationById<>();
        specification.setId(id);
        return repository().findOne(specification);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<List<T>> findAll(Specification<T> specification) {
        Objects.requireNonNull(specification);
        List<T> all = repository().findAll(specification);
        return Optional.of(all);
    }

    @Override
    public Optional<List<T>> findAll() {
        return Optional.of(repository().findAll());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<List<T>> findByIds(List<ID> ids, Class<ID> idClass) {
        Objects.requireNonNull(ids, "唯一标识集合不能是空的");
        return findAll(((r, q, cb) -> {
            Expression<ID> exp = r.get("id").as(idClass);
            return exp.in(ids);
        }));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean exists(Specification<T> spec) {
        Objects.requireNonNull(spec);
        return repository().exists(spec);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<T> findOne(Specification<T> spec) {
        Objects.requireNonNull(spec);
        return repository().findOne(spec);
    }

    @Override
    public Page<T> page(Specification<T> spec, int page, int size) {
        if (page < 1 || size < 0) {
            throw new RuntimeException("请检查分页参数，参数范围【page >= 1,size >= 0】");
        }
        Pageable pageable = PageRequest.of(page - 1, size);
        if (spec == null) return repository().findAll(pageable);
        return repository().findAll(spec, pageable);
    }

    @Override
    public boolean existsById(ID id) {
        return repository().existsById(id);
    }
}
